As a resident of Melbourne, I want to input my current location and the places of interest I plan to visit so that I can identify the nearest parking bays to these locations, helping me better understand the parking availability and proximity to my chosen destinations.
At the end of this use case you will:
- Learn to work with categorical locations and geolocations using geopy.
- Learn foundational NLP techniques, including corpus creation, using regular expressions to remove unnecessary symbols, tokenization, stopword removal, and applying TF-IDF vectorization.
- Learn to use cosine similarity and haversine distance.
- Learn to plot multiple geolocations on a map using folium.
- Learn to display multiple maps on different tabs using folium.
The ninth Sustainable Development Goal (SDG) of the City of Melbourne, "Industry, Innovation, and Infrastructure," emphasizes the development of affordable and equitable transport infrastructure for all. This includes ensuring the availability and adequacy of parking facilities around popular places of interest.
In this use case, users can input their current location and desired places of interest. The code will then identify up to 10 top matches within a 2km radius of the user’s location. For each suggested place, a map will display all parking bays within a 200m radius. This functionality will help users plan their journeys more effectively by providing them with a visual overview of parking options near their destinations. It ensures that users can easily find convenient parking spots, enhancing their overall travel experience.
This use case also aligns with one of the eight key priorities of the Economic Development Strategy 2031 for the City of Melbourne: creating a digitally connected city. By leveraging smart technology and connectivity, this initiative supports Melbourne’s vision of becoming a knowledge-enabled, smart city, adapting to modern connectivity needs and enhancing urban infrastructure.
This dataset mainly contains the theme, sub_theme, feature_name and coordinates of various places of interest throughout the city of Melbourne. This dataset is used to create the corpus and filter suggested places of interest based on user inputs, identifying options within a 2km radius of the user’s location. This dataset is imported from Melbourne Open Data website, using API V2.1.
This dataset contains the roadsegmentid, kerbsideid, roadsegmentdescription, latitude, longitude and last updated details of on-street parking bays throughout the city of Melbourne. The longitude and latitude features of this dataset are used to identify parking bays within a 200m radius of each suggested place of interest. This dataset is imported from Melbourne Open Data website, using API V2.1.
import requests
import pandas as pd
import numpy as np
from io import StringIO
import seaborn as sns
import matplotlib.pyplot as plt
import nltk
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
import re
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import folium
from folium.plugins import MarkerCluster
import math
import geopy
from geopy.geocoders import Nominatim
from IPython.display import display, HTML
import ipywidgets as widgets
import warnings
warnings.filterwarnings("ignore")
nltk.download('stopwords')
nltk.download('punkt')
[nltk_data] Downloading package stopwords to [nltk_data] /Users/samihahaque/nltk_data... [nltk_data] Package stopwords is already up-to-date! [nltk_data] Downloading package punkt to [nltk_data] /Users/samihahaque/nltk_data... [nltk_data] Package punkt is already up-to-date!
True
Loading the datasets using API 2.1v¶
base_url='https://data.melbourne.vic.gov.au/api/explore/v2.1/catalog/datasets/'
dataset_id='landmarks-and-places-of-interest-including-schools-theatres-health-services-spor'
url=f'{base_url}{dataset_id}/exports/csv'
params={'select':'*','limit':-1,'lang':'en','timezone':'UTC'}
response=requests.get(url,params=params)
if response.status_code==200:
url_content=response.content.decode('utf-8')
places=pd.read_csv(StringIO(url_content),delimiter=';')
print(places.head(10))
else:
print(f'Request failed with status code {response.status_code}')
theme sub_theme feature_name \
0 Place of Worship Church St Francis Church
1 Place of Worship Church St James Church
2 Place of Worship Church St Mary's Anglican Church
3 Place of Worship Church Scots Church
4 Place of Worship Church St Michael's Uniting Church
5 Place of Worship Church Greek Orthodox Church
6 Place of Worship Church North Melbourne Uniting
7 Place of Worship Church South Yarra Presbyterian Church
8 Place of Worship Synagogue East Melbourne Synagogue
9 Transport Transport Terminal Port of Melbourne
co_ordinates
0 -37.8118847831837, 144.962422614541
1 -37.8101281201969, 144.952468571683
2 -37.8031663672997, 144.953761537074
3 -37.8145687802664, 144.96855105335
4 -37.8143851324913, 144.969174036096
5 -37.8088064667555, 144.978259089269
6 -37.8035538471344, 144.947671538375
7 -37.8407473645397, 144.98562699348
8 -37.809113728917, 144.97422190954
9 -37.8137384362671, 144.91753432375
base_url='https://data.melbourne.vic.gov.au/api/explore/v2.1/catalog/datasets/'
dataset_id='on-street-parking-bays'
url=f'{base_url}{dataset_id}/exports/csv'
params={'select':'*','limit':-1,'lang':'en','timezone':'UTC'}
response=requests.get(url,params=params)
if response.status_code==200:
url_content=response.content.decode('utf-8')
parking_bays=pd.read_csv(StringIO(url_content),delimiter=';')
print(parking_bays.head(10))
else:
print(f'Request failed with status code {response.status_code}')
roadsegmentid kerbsideid \
0 22730 NaN
1 22730 NaN
2 20013 5701
3 20013 23444
4 22268 NaN
5 22295 NaN
6 22295 NaN
7 22295 NaN
8 21108 NaN
9 20950 NaN
roadsegmentdescription latitude longitude \
0 Park Street between Mason Street and Randall P... -37.836245 144.982021
1 Park Street between Mason Street and Randall P... -37.835800 144.982115
2 Lonsdale Street between William Street and Kin... -37.814238 144.955451
3 Lonsdale Street between William Street and Kin... -37.814271 144.955334
4 Clowes Street between Anderson Street and Wals... -37.830568 144.984713
5 Anderson Street between Domain Road and Acland... -37.833607 144.983763
6 Anderson Street between Domain Road and Acland... -37.833657 144.983753
7 Anderson Street between Domain Road and Acland... -37.833817 144.983720
8 Courtney Street between Queensberry Street and... -37.803262 144.955425
9 Queensberry Street between Capel Street and Ho... -37.803515 144.954739
lastupdated
0 2022-08-31
1 2022-08-31
2 2023-10-02
3 2023-10-02
4 2022-08-31
5 2022-08-31
6 2022-08-31
7 2022-08-31
8 2022-08-31
9 2022-08-31
coordinates function to return latitude and longitude given any categorical location¶
def coordinates(location):
geolocator = Nominatim(user_agent="geocoding")
loc = geolocator.geocode(location)
if loc:
return loc.latitude, loc.longitude
else:
raise Exception("Geocoding failed")
haversine function to return the haversine distance given a pair of latitudea and longitudes¶
def haversine(lat1, lon1, lat2, lon2):
R = 6371 # Radius of Earth in kilometers
dlat = math.radians(lat2 - lat1)
dlon = math.radians(lon2 - lon1)
a = math.sin(dlat / 2) ** 2 + math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) * math.sin(dlon / 2) ** 2
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
distance = R * c
return distance
create_map function to create map and markers¶
def create_map(map_display,df,popup,color,marker=False):
if marker:
marker_cluster = MarkerCluster().add_to(map_display)
# Add markers for each location
for index, row in df.iterrows():
folium.Marker(location=[row['latitude'], row['longitude']],
popup=row[popup],
icon=folium.Icon(color),
icon_size=(3, 3)).add_to(marker_cluster)
else:
for index, row in df.iterrows():
folium.Marker(location=[row['latitude'], row['longitude']],
popup=row[popup],
icon=folium.Icon(color),
icon_size=(3, 3)).add_to(map_display)
return map_display
Cleaning Places dataset¶
places
| theme | sub_theme | feature_name | co_ordinates | |
|---|---|---|---|---|
| 0 | Place of Worship | Church | St Francis Church | -37.8118847831837, 144.962422614541 |
| 1 | Place of Worship | Church | St James Church | -37.8101281201969, 144.952468571683 |
| 2 | Place of Worship | Church | St Mary's Anglican Church | -37.8031663672997, 144.953761537074 |
| 3 | Place of Worship | Church | Scots Church | -37.8145687802664, 144.96855105335 |
| 4 | Place of Worship | Church | St Michael's Uniting Church | -37.8143851324913, 144.969174036096 |
| ... | ... | ... | ... | ... |
| 237 | Education Centre | School - Primary and Secondary Education | Melbourne Girls Grammar School | -37.8315364518803, 144.985089428348 |
| 238 | Retail | Department Store | Myer | -37.8135911985281, 144.963855087868 |
| 239 | Retail | Department Store | David Jones | -37.8133127260638, 144.964373486798 |
| 240 | Health Services | Medical Services | Mercy Private Hospital | -37.811896809802, 144.984435746587 |
| 241 | Mixed Use | Retail/Office/Carpark | ANZ 'Gothic' Bank | -37.8161580666029, 144.961673719242 |
242 rows × 4 columns
places.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 242 entries, 0 to 241 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 theme 242 non-null object 1 sub_theme 242 non-null object 2 feature_name 242 non-null object 3 co_ordinates 242 non-null object dtypes: object(4) memory usage: 7.7+ KB
places.shape
(242, 4)
places.isna().sum()
theme 0 sub_theme 0 feature_name 0 co_ordinates 0 dtype: int64
places['theme'].value_counts()
theme Leisure/Recreation 63 Place Of Assembly 40 Place of Worship 31 Transport 26 Community Use 21 Education Centre 13 Mixed Use 11 Health Services 11 Office 11 Purpose Built 4 Vacant Land 3 Retail 3 Residential Accommodation 2 Specialist Residential Accommodation 1 Warehouse/Store 1 Industrial 1 Name: count, dtype: int64
places['sub_theme'].value_counts()
sub_theme Informal Outdoor Facility (Park/Garden/Reserve) 37 Church 30 Railway Station 23 Art Gallery/Museum 19 Theatre Live 15 Major Sports & Recreation Facility 14 Public Buildings 13 Office 11 Public Hospital 7 Retail/Office/Carpark 5 Tertiary (University) 4 Primary Schools 4 Outdoor Recreation Facility (Zoo, Golf Course) 4 Function/Conference/Exhibition Centre 4 Indoor Recreation Facility 3 Police Station 3 Private Hospital 3 Retail/Office 3 Dwelling (House) 2 Visitor Centre 2 Private Sports Club/Facility 2 School - Primary and Secondary Education 2 Department Store 2 Retail/Office/Residential/Carpark 2 Observation Tower/Wheel 2 Film & RV Studio 2 Secondary Schools 2 Synagogue 1 Cemetery 1 Transport Terminal 1 Bridge 1 Aquarium 1 Industrial (Manufacturing) 1 Cinema 1 Current Construction Site - Commercial 1 Casino 1 Retail 1 Library 1 Fire Station 1 Government Building 1 Retail/Residential 1 Gymnasium/Health Club 1 Vacant Land - Undeveloped Site 1 Store Yard 1 Further Education 1 Current Construction Site 1 Marina 1 Hostel 1 Medical Services 1 Name: count, dtype: int64
for i in places['theme'].unique():
if len(places.loc[places['theme']==i].value_counts())<5:
print(places.loc[places['theme']==i])
print("------------------------------------------")
theme sub_theme \
23 Vacant Land Vacant Land - Undeveloped Site
118 Vacant Land Current Construction Site
205 Vacant Land Current Construction Site - Commercial
feature_name \
23 Melbourne International Karting Complex
118 Harbour Town
205 Railway Good Shed No 2
co_ordinates
23 -37.8310746380616, 144.913822928701
118 -37.8139256326845, 144.938123825625
205 -37.8211371302179, 144.951378883631
------------------------------------------
theme sub_theme feature_name \
33 Purpose Built Film & RV Studio Central City Studios
117 Purpose Built Film & RV Studio Channel 7 - Melbourne Broadcast Centre
198 Purpose Built Casino Crown Entertainment Complex
233 Purpose Built Aquarium Melbourne Aquarium
co_ordinates
33 -37.8142318521296, 144.935655305362
117 -37.8158572333481, 144.94582326909
198 -37.8235761092516, 144.957312880653
233 -37.8209627824685, 144.958425696055
------------------------------------------
theme sub_theme \
37 Specialist Residential Accommodation Hostel
feature_name co_ordinates
37 The Mission To Seafarers Victoria -37.8224995970811, 144.951254841448
------------------------------------------
theme sub_theme feature_name \
85 Residential Accommodation Dwelling (House) Bishopscourt
133 Residential Accommodation Dwelling (House) Government House
co_ordinates
85 -37.8132921649416, 144.98350690799
133 -37.8275635886174, 144.976655130464
------------------------------------------
theme sub_theme feature_name \
146 Warehouse/Store Store Yard Melbourne Wholesale Fish Market
co_ordinates
146 -37.8083471006041, 144.930613255612
------------------------------------------
theme sub_theme feature_name \
179 Retail Retail Queen Victoria Market
238 Retail Department Store Myer
239 Retail Department Store David Jones
co_ordinates
179 -37.8075002018073, 144.957158515056
238 -37.8135911985281, 144.963855087868
239 -37.8133127260638, 144.964373486798
------------------------------------------
theme sub_theme feature_name \
225 Industrial Industrial (Manufacturing) Kraft
co_ordinates
225 -37.8262313085528, 144.923475698195
------------------------------------------
drop_themes=['Vacant Land','Purpose Built','Specialist Residential Accommodation','Residential Accommodation','Warehouse/Store','Industrial']
places=places[~places['theme'].isin(drop_themes)]
print(places.shape)
places.head()
(230, 4)
| theme | sub_theme | feature_name | co_ordinates | |
|---|---|---|---|---|
| 0 | Place of Worship | Church | St Francis Church | -37.8118847831837, 144.962422614541 |
| 1 | Place of Worship | Church | St James Church | -37.8101281201969, 144.952468571683 |
| 2 | Place of Worship | Church | St Mary's Anglican Church | -37.8031663672997, 144.953761537074 |
| 3 | Place of Worship | Church | Scots Church | -37.8145687802664, 144.96855105335 |
| 4 | Place of Worship | Church | St Michael's Uniting Church | -37.8143851324913, 144.969174036096 |
co_ord_list = places['co_ordinates'].str.split(", ", expand=True)
places.loc[:, 'latitude'] = pd.to_numeric(co_ord_list[0])
places.loc[:, 'longitude'] = pd.to_numeric(co_ord_list[1])
places.drop(columns='co_ordinates',axis=1,inplace=True)
places.reset_index(drop=True, inplace=True)
for i in places.columns[:-2]:
places.loc[:,i]=places[i].str.lower()
places.head()
| theme | sub_theme | feature_name | latitude | longitude | |
|---|---|---|---|---|---|
| 0 | place of worship | church | st francis church | -37.811885 | 144.962423 |
| 1 | place of worship | church | st james church | -37.810128 | 144.952469 |
| 2 | place of worship | church | st mary's anglican church | -37.803166 | 144.953762 |
| 3 | place of worship | church | scots church | -37.814569 | 144.968551 |
| 4 | place of worship | church | st michael's uniting church | -37.814385 | 144.969174 |
Creating the "corpus" column¶
corpus=places.loc[:,'theme']+" "+places.loc[:,'sub_theme']+" "+places.loc[:,'feature_name']
places.loc[:,'corpus']=corpus
pattern = r'[^a-zA-Z0-9\s]'
places.loc[:,'corpus'] = places['corpus'].str.replace(pattern, ' ', regex=True)
# places.loc[:,'corpus']=places['corpus'].str.replace(r'\s+', ' ', regex=True).str.strip()
places.loc[:,'corpus_token']=places['corpus'].apply(word_tokenize)
stop_words=stopwords.words('english')
lst=[]
for i in range(len(places['corpus_token'])):
st=""
for j in places.loc[i,'corpus_token']:
if j not in stop_words:
st+=j+" "
lst.append(st[:-1])
places.loc[:,'corpus_clean']=lst
places
| theme | sub_theme | feature_name | latitude | longitude | corpus | corpus_token | corpus_clean | |
|---|---|---|---|---|---|---|---|---|
| 0 | place of worship | church | st francis church | -37.811885 | 144.962423 | place of worship church st francis church | [place, of, worship, church, st, francis, church] | place worship church st francis church |
| 1 | place of worship | church | st james church | -37.810128 | 144.952469 | place of worship church st james church | [place, of, worship, church, st, james, church] | place worship church st james church |
| 2 | place of worship | church | st mary's anglican church | -37.803166 | 144.953762 | place of worship church st mary s anglican church | [place, of, worship, church, st, mary, s, angl... | place worship church st mary anglican church |
| 3 | place of worship | church | scots church | -37.814569 | 144.968551 | place of worship church scots church | [place, of, worship, church, scots, church] | place worship church scots church |
| 4 | place of worship | church | st michael's uniting church | -37.814385 | 144.969174 | place of worship church st michael s uniting c... | [place, of, worship, church, st, michael, s, u... | place worship church st michael uniting church |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 225 | education centre | school - primary and secondary education | melbourne girls grammar school | -37.831536 | 144.985089 | education centre school primary and secondar... | [education, centre, school, primary, and, seco... | education centre school primary secondary educ... |
| 226 | retail | department store | myer | -37.813591 | 144.963855 | retail department store myer | [retail, department, store, myer] | retail department store myer |
| 227 | retail | department store | david jones | -37.813313 | 144.964373 | retail department store david jones | [retail, department, store, david, jones] | retail department store david jones |
| 228 | health services | medical services | mercy private hospital | -37.811897 | 144.984436 | health services medical services mercy private... | [health, services, medical, services, mercy, p... | health services medical services mercy private... |
| 229 | mixed use | retail/office/carpark | anz 'gothic' bank | -37.816158 | 144.961674 | mixed use retail office carpark anz gothic bank | [mixed, use, retail, office, carpark, anz, got... | mixed use retail office carpark anz gothic bank |
230 rows × 8 columns
TfidfVectorizer¶
vectorizer = TfidfVectorizer(stop_words='english')
tfidf_matrix = vectorizer.fit_transform(places['corpus_clean'])
Processing user input¶
def user_input(place,user_loc):
user_input_vector = vectorizer.transform([place])
l1,l2=coordinates(user_loc)
return user_input_vector,l1,l2
cosine similarity to filter out records which are similar to the user input¶
def get_filtered_places(user_input_vector,tfidf_matrix,places,l1,l2):
similarities=cosine_similarity(user_input_vector, tfidf_matrix)
top_ind=np.argsort(similarities[0])[::-1]
similar_indices=[]
for i in top_ind:
if similarities[0][i]>0:
similar_indices.append(i)
relevant_records = places.iloc[similar_indices]
relevant_records.reset_index(drop=True,inplace=True)
#Applying haversine distance to filter the records(at max 10) which are within 2km of user's location
ind=[]
for i,j in relevant_records.iterrows():
distance=haversine(l1,l2,j['latitude'],j['longitude'])
if distance<=2 and len(ind)<10:
ind.append(i)
relevant_records_filtered = relevant_records.iloc[ind]
return relevant_records_filtered
===============================================================================================================
Preprocessing the Parking_bays dataset¶
parking_bays
| roadsegmentid | kerbsideid | roadsegmentdescription | latitude | longitude | lastupdated | |
|---|---|---|---|---|---|---|
| 0 | 22730 | NaN | Park Street between Mason Street and Randall P... | -37.836245 | 144.982021 | 2022-08-31 |
| 1 | 22730 | NaN | Park Street between Mason Street and Randall P... | -37.835800 | 144.982115 | 2022-08-31 |
| 2 | 20013 | 5701 | Lonsdale Street between William Street and Kin... | -37.814238 | 144.955451 | 2023-10-02 |
| 3 | 20013 | 23444 | Lonsdale Street between William Street and Kin... | -37.814271 | 144.955334 | 2023-10-02 |
| 4 | 22268 | NaN | Clowes Street between Anderson Street and Wals... | -37.830568 | 144.984713 | 2022-08-31 |
| ... | ... | ... | ... | ... | ... | ... |
| 19157 | 22492 | NaN | Alexandra Avenue between Swan Street Bridge an... | -37.827226 | 144.980441 | 2022-08-31 |
| 19158 | 22492 | NaN | Alexandra Avenue between Swan Street Bridge an... | -37.827380 | 144.981004 | 2022-08-31 |
| 19159 | 22492 | NaN | Alexandra Avenue between Swan Street Bridge an... | -37.826361 | 144.979266 | 2022-08-31 |
| 19160 | 22492 | NaN | Alexandra Avenue between Swan Street Bridge an... | -37.826780 | 144.979662 | 2022-08-31 |
| 19161 | 22492 | NaN | Alexandra Avenue between Swan Street Bridge an... | -37.825850 | 144.978719 | 2022-08-31 |
19162 rows × 6 columns
parking_bays.shape
(19162, 6)
parking_bays.isna().sum()
roadsegmentid 0 kerbsideid 14149 roadsegmentdescription 0 latitude 0 longitude 0 lastupdated 0 dtype: int64
parking_bays.drop(columns='kerbsideid',inplace=True)
parking_bays.isna().sum()
roadsegmentid 0 roadsegmentdescription 0 latitude 0 longitude 0 lastupdated 0 dtype: int64
Applying haversine distance to filter the parking bays which are within 200m of filtered places of interest¶
def get_filtered_parking(parking_bays,relevant_records_filtered):
ind=[]
for i,j in parking_bays.iterrows():
for m,n in relevant_records_filtered.iterrows():
distance=haversine(j['latitude'],j['longitude'],n['latitude'],n['longitude'])
if distance<=0.2 and i not in ind:
ind.append(i)
parking_bays_filtered = parking_bays.iloc[ind]
parking_bays_filtered.reset_index(drop=True,inplace=True)
return parking_bays_filtered
Displaying all places of interest and filtered places, all parking bays, filtered places and closest parking bays on a map.¶
place_name = input("Enter a place of interest: ").lower()
user_loc=input("Enter your location: ").lower()
user_input_vector,user_l1,user_l2=user_input(place_name,user_loc)
relevant_records_filtered=get_filtered_places(user_input_vector,tfidf_matrix,places,user_l1,user_l2)
parking_bays_filtered=get_filtered_parking(parking_bays,relevant_records_filtered)
map_display1 = folium.Map(location=[places['latitude'].mean(), places['longitude'].mean()], zoom_start=10)
map_display1=create_map(map_display1,places,'feature_name','blue')
map_display1=create_map(map_display1,relevant_records_filtered,'feature_name','green')
legend_html1 = """
<div style="position: fixed;
bottom: 50px; left: 50px; width: 150px; height: 60px;
border:2px solid grey; z-index:9999; font-size:14px;
background-color:white; opacity: 0.8;">
<p style="text-align:center; margin: 0;"><strong>Legend</strong></p>
<p style="margin: 0;">All places of interest: <span style="color:lightblue">●</span></p>
<p style="margin: 0;">Filtered places: <span style="color:green">●</span></p>
</div>
"""
title_html1 = """
<h3 style="text-align: center; margin: 10px 0;">All places of interest and filtered places of interest</h3>
"""
map_display1.get_root().html.add_child(folium.Element(legend_html1))
map_display1.get_root().html.add_child(folium.Element(title_html1))
# map_html_places = map_display1._repr_html_()
#-------------------------------------------------------------------
map_display2 = folium.Map(location=[parking_bays['latitude'].mean(), parking_bays['longitude'].mean()], zoom_start=10)
map_display2=create_map(map_display2,parking_bays,'roadsegmentdescription','blue',marker=True)
legend_html2 = """
<div style="position: fixed;
bottom: 50px; left: 50px; width: 150px; height:50px;
border:2px solid grey; z-index:9999; font-size:14px;
background-color:white; opacity: 0.8;">
<p style="text-align:center; margin: 0;"><strong>Legend</strong></p>
<p style="margin: 0;">Parking Spots: <span style="color:lightblue">●</span></p>
</div>
"""
title_html2 = """
<h3 style="text-align: center; margin: 10px 0;">Parking Bays</h3>
"""
map_display2.get_root().html.add_child(folium.Element(legend_html2))
map_display2.get_root().html.add_child(folium.Element(title_html2))
# map_html_parking = map_display2._repr_html_()
#-------------------------------------------------------------------
map_display3 = folium.Map(location=[relevant_records_filtered['latitude'].mean(), relevant_records_filtered['longitude'].mean()], zoom_start=10)
map_display3=create_map(map_display3,parking_bays_filtered,'roadsegmentdescription','orange',marker=True)
map_display3=create_map(map_display3,relevant_records_filtered,'feature_name','blue')
legend_html3 = """
<div style="position: fixed;
bottom: 50px; left: 50px; width: 150px; height: 90px;
border:2px solid grey; z-index:9999; font-size:14px;
background-color:white; opacity: 0.8;">
<p style="text-align:center; margin: 0;"><strong>Legend</strong></p>
<p style="margin: 0;">Places of interest: <span style="color:lightblue">●</span></p>
</div>
"""
title_html3 = """
<h3 style="text-align: center; margin: 10px 0;">Filtered places and closest parking bays</h3>
"""
map_display3.get_root().html.add_child(folium.Element(legend_html3))
map_display3.get_root().html.add_child(folium.Element(title_html3))
# map_html_parking_places = map_display3._repr_html_()
#-------------------------------------------------------------------
# tab1 = widgets.Output()
# tab2 = widgets.Output()
# tab3 = widgets.Output()
# with tab1:
# display(HTML(map_html_places))
# with tab2:
# display(HTML(map_html_parking))
# with tab3:
# display(HTML(map_html_parking_places))
# # Create a tab layout
# tabs = widgets.Tab(children=[tab3])
# tabs.set_title(0, 'All places vs filtered places')
# # tabs.set_title(1, 'Parking bays')
# # tabs.set_title(2, 'Filtered places and closest parking')
# display(tabs)
<branca.element.Element at 0x179d17f70>
display(map_display1)
display(map_display2)